/*
 * FileName: MemoryTools.java
 * Author:   zzw
 * Date:     2018年11月19日
 * Description:
 */
package com.zzw.hotspot.sa;

import sun.jvm.hotspot.gc_implementation.g1.G1CollectedHeap;
import sun.jvm.hotspot.gc_implementation.g1.G1MonitoringSupport;
import sun.jvm.hotspot.gc_implementation.g1.HeapRegion;
import sun.jvm.hotspot.gc_implementation.g1.HeapRegionSetBase;
import sun.jvm.hotspot.gc_implementation.parallelScavenge.PSOldGen;
import sun.jvm.hotspot.gc_implementation.parallelScavenge.PSYoungGen;
import sun.jvm.hotspot.gc_implementation.parallelScavenge.ParallelScavengeHeap;
import sun.jvm.hotspot.gc_implementation.shared.MutableSpace;
import sun.jvm.hotspot.gc_interface.CollectedHeap;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.HeapPrinter;
import sun.jvm.hotspot.oops.HeapVisitor;
import sun.jvm.hotspot.oops.ObjectHeap;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

import java.util.HashMap;
import java.util.Map;

/**
 * 〈〉<br>
 * 〈〉
 *
 * @author zzw
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本]（可选）
 */
public class MemoryTools extends Tool {
    private static String alignment = "   ";
    private static final double FACTOR = 1024*1024;

    @Override
    public void run() {
        VM vm = VM.getVM();
        VM.Flag[] flags = VM.getVM().getCommandLineFlags();
        Universe universe = vm.getUniverse();
        CollectedHeap heap = universe.heap();
        puts("GC heap name: " + heap.kind());
        System.out.println("Heap Configuration:");
        Map flagMap = new HashMap();
        if (flags == null) {
            System.out.println("WARNING: command line flags are not available");
        } else {
            for (int f = 0; f < flags.length; f++) {
                if ("PermSize".equals(flags[f].getName())) {
                    break;
                }
                if (flags[f].getName().contains("Meta")) {
                    System.out.println(flags[f].getName() + ":" + flags[f].getValue());
                }
                flagMap.put(flags[f].getName(), flags[f]);
            }
        }

        System.out.println();
        printGCAlgorithm(flagMap);

        printValue("MinHeapFreeRatio         = ", getFlagValue("MinHeapFreeRatio", flagMap));
        printValue("MaxHeapFreeRatio         = ", getFlagValue("MaxHeapFreeRatio", flagMap));
        printValMB("MaxHeapSize              = ", getFlagValue("MaxHeapSize", flagMap));
        printValMB("NewSize                  = ", getFlagValue("NewSize", flagMap));
        printValMB("MaxNewSize               = ", getFlagValue("MaxNewSize", flagMap));
        printValMB("OldSize                  = ", getFlagValue("OldSize", flagMap));
        printValue("NewRatio                 = ", getFlagValue("NewRatio", flagMap));
        printValue("SurvivorRatio            = ", getFlagValue("SurvivorRatio", flagMap));
        printValMB("MetaspaceSize            = ", getFlagValue("MetaspaceSize", flagMap));
        printValMB("CompressedClassSpaceSize = ", getFlagValue("CompressedClassSpaceSize", flagMap));
        printValMB("MaxMetaspaceSize         = ", getFlagValue("MaxMetaspaceSize", flagMap));
        printValMB("G1HeapRegionSize         = ", HeapRegion.grainBytes());
        if (heap instanceof ParallelScavengeHeap) {
            ParallelScavengeHeap psHeap = (ParallelScavengeHeap) heap;
            PSOldGen psOldGen = psHeap.oldGen();
            MutableSpace oldSpace = psOldGen.objectSpace();
            puts("Old gen: [" + oldSpace.bottom() + ", " + oldSpace.end() + ")");
            PSYoungGen psYoungGen = psHeap.youngGen();
            MutableSpace edenSpace = psYoungGen.edenSpace();
            MutableSpace fromSpace = psYoungGen.fromSpace();
            MutableSpace toSpace = psYoungGen.toSpace();
            System.out.println("PS Old Generation");
            long oldFree = psOldGen.capacity() - psOldGen.used();
            printValMB("capacity = ", psOldGen.capacity());
            printValMB("used     = ", psOldGen.used());
            printValMB("free     = ", oldFree);
            System.out.println(alignment + (double)psOldGen.used() * 100.0 / psOldGen.capacity() + "% used");
            System.out.println("PS Young Generation");
            puts("Eden gen: [" + edenSpace.bottom() + ", " + edenSpace.end() + ")");
            System.out.println("Eden Space:");
            printMutableSpace(edenSpace);
            puts("From: [" + fromSpace.bottom() + ", " + fromSpace.end() + ")");
            System.out.println("From Space:");
            printMutableSpace(fromSpace);
            puts("To gen: [" + toSpace.bottom() + ", " + toSpace.end() + ")");
            System.out.println("To Space:");
            printMutableSpace(toSpace);

        }
        //MarkSweepPolicy/ASConcurrentMarkSweepPolicy/ConcurrentMarkSweepPolicy 标记清除
        else if(heap instanceof GenCollectedHeap){
            GenCollectedHeap genCollectedHeap = (GenCollectedHeap) heap;
            int i = genCollectedHeap.nGens();
            for(int j = 0;j < i;j ++){
                Generation gen = genCollectedHeap.getGen(j);
                if (gen instanceof ConcurrentMarkSweepGeneration){
                    System.out.println(gen.name());
                    System.out.println("Old Space:");
                    printGen(gen);
                }
                else if(gen instanceof DefNewGeneration){
                    System.out.println("New Generation (Eden + 1 Survivor Space):");
                    printGen(gen);

                    ContiguousSpace eden = ((DefNewGeneration)gen).eden();
                    System.out.println("Eden Space:");
                    printSpace(eden);

                    ContiguousSpace from = ((DefNewGeneration)gen).from();
                    System.out.println("From Space:");
                    printSpace(from);

                    ContiguousSpace to = ((DefNewGeneration)gen).to();
                    System.out.println("To Space:");
                    printSpace(to);
                }
            }
        }
        else if(heap instanceof G1CollectedHeap){
            G1CollectedHeap g1CollectedHeap = (G1CollectedHeap) heap;
            G1MonitoringSupport g1MonitoringSupport = g1CollectedHeap.g1mm();
            HeapRegionSetBase heapRegionSetBase = g1CollectedHeap.humongousSet();
            HeapRegionSetBase heapRegionSetBase1 = g1CollectedHeap.oldSet();
            MemRegion memRegion = g1CollectedHeap.reservedRegion();
        }
        puts();
        ObjectHeap objHeap = vm.getObjectHeap();
        HeapVisitor heapVisitor = new HeapPrinter(System.out);
        objHeap.iterate(heapVisitor);
    }

    private static void puts() {
        System.out.println();
    }

    private static void puts(String s) {
        System.out.println(s);
    }

    private void printSpace(ContiguousSpace space) {
        printValMB("capacity = ", space.capacity());
        printValMB("used     = ", space.used());
        printValMB("free     = ", space.free());
        System.out.println(alignment +  (double)space.used() * 100.0 / space.capacity() + "% used");
    }

    private void printValMB(String title, long value) {
        if (value < 0) {
            System.out.println(alignment + title +   (value >>> 20)  + " MB");
        } else {
            double mb = value/FACTOR;
            System.out.println(alignment + title + value + " (" + mb + "MB)");
        }
    }

    private void printValue(String title, long value) {
        System.out.println(alignment + title + value);
    }

    private long getFlagValue(String name, Map flagMap) {
        VM.Flag f = (VM.Flag) flagMap.get(name);
        if (f != null) {
            if (f.isBool()) {
                return f.getBool()? 1L : 0L;
            } else {
                return Long.parseLong(f.getValue());
            }
        } else {
            return -1;
        }
    }

    private void printGen(Generation gen) {
        printValMB("capacity = ", gen.capacity());
        printValMB("used     = ", gen.used());
        printValMB("free     = ", gen.free());
        System.out.println(alignment + (double)gen.used() * 100.0 / gen.capacity() + "% used");
    }

    private void printGCAlgorithm(Map flagMap) {
        // print about new generation
        long l = getFlagValue("UseParNewGC", flagMap);
        if (l == 1L) {
            System.out.println("using parallel threads in the new generation.");
        }

        l = getFlagValue("UseTLAB", flagMap);
        if (l == 1L) {
            System.out.println("using thread-local object allocation.");
        }

        l = getFlagValue("UseConcMarkSweepGC", flagMap);
        if (l == 1L) {
            System.out.println("Concurrent Mark-Sweep GC");
            return;
        }

        l = getFlagValue("UseParallelGC", flagMap);
        if (l == 1L) {
            System.out.print("Parallel GC ");
            l = getFlagValue("ParallelGCThreads", flagMap);
            System.out.println("with " + l + " thread(s)");
            return;
        }

        l = getFlagValue("UseG1GC", flagMap);
        if (l == 1L) {
            System.out.print("Garbage-First (G1) GC ");
            l = getFlagValue("ParallelGCThreads", flagMap);
            System.out.println("with " + l + " thread(s)");
            return;
        }

        System.out.println("Mark Sweep Compact GC");
    }

    private void printMutableSpace(MutableSpace space) {
        printValMB("capacity = ", space.capacity());
        printValMB("used     = ", space.used());
        long free = space.capacity() - space.used();
        printValMB("free     = ", free);
        System.out.println(alignment + (double)space.used() * 100.0 / space.capacity() + "% used");
    }



    public static void main(String[] args) {
        MemoryTools tool = new MemoryTools();
        tool.start(args);
        tool.stop();
    }
}
